package com.zenglx.multilanguage.provider;

import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.zenglx.multilanguage.exception.MapperException;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.List;
import java.util.Map;

/**
 * SnowflakeKeyGenerator
 *
 * @author liangxi.zeng
 */
public class SnowflakeKeyGenerator implements KeyGenerator {
    protected DefaultIdentifierGenerator identifierGenerator;

    public SnowflakeKeyGenerator(DefaultIdentifierGenerator identifierGenerator) {
        if (identifierGenerator == null) {
            throw new MapperException("com.tcl.mybatis.helper.snowflake.SnowflakeKeyGenerator.SnowflakeKeyGenerator snowflakeHelper null ");
        }
        this.identifierGenerator = identifierGenerator;
    }

    @Override
    public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
        if (parameter != null && ms != null && ms.getConfiguration() != null) {
            final MetaObject parameterMeta = ms.getConfiguration().newMetaObject(parameter);
            String[] keyProperties = ms.getKeyProperties();
            if (keyProperties.length != 1) {
                throw new MapperException("Snowflake ID does not support composite primary keys.");
            }
            if (!existsKeyValues(parameterMeta, keyProperties, parameter)) {
                parameterMeta.setValue(keyProperties[0], generateKey());
            }else if(!existsKeyListValues(parameterMeta,keyProperties)){
                setListValues(parameter,keyProperties[0]);
            }
        }
    }

    private void setListValues(Object parameter,String keyProperties) {
        if (!(parameter instanceof Map)) {
            throw new MapperException("Snowflake ID does not support this parameter.");
        }
        Map<String, Object> map = (Map<String, Object>) parameter;
        Object list = map.get("list");
        if (!(list instanceof List)) {
            throw new MapperException("Snowflake ID does not support this parameter.");
        }
        List<Object> params = (List<Object>) list;
        for (Object param : params) {
            Class<?> clazz = param.getClass();
            try {
                Field declaredField = clazz.getDeclaredField(keyProperties);
                declaredField.setAccessible(true);
                declaredField.set(param,generateKey());
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new MapperException("Snowflake ID does not support this parameter.");
            }
        }
    }

    private boolean existsKeyListValues(MetaObject metaParam, String[] keyProperties) {
        if (keyProperties == null || keyProperties.length == 0) {
            return true;
        }
        for (String keyProperty : keyProperties) {
            if (!metaParam.hasGetter("list")) {
                return true;
            }
            Object obj = metaParam.getValue("list");
            if (!(obj instanceof List)) {
                return true;
            }
            List<Object> values = (List<Object>) obj;
            if(CollectionUtils.isEmpty(values)){
                return true;
            }
            for (Object value : values) {
                Class<?> clazz = value.getClass();
                try {
                    Field field = clazz.getDeclaredField(keyProperty);
                    field.setAccessible(true);
                    //如果该字段有值，则用原来的值，不生成
                    if(field.get(value) != null){
                        return true;
                    }
                } catch (NoSuchFieldException | IllegalAccessException e) {
                }
            }
        }
        return false;
    }


    private Long generateKey() {
        return identifierGenerator.nextId(null);
    }

    private boolean existsKeyValues(MetaObject metaParam, String[] keyProperties, Object parameter) {
        if (keyProperties == null || keyProperties.length == 0) {
            return true;
        }
        for (String property : keyProperties) {
            if (metaParam.hasGetter(property)) {
                Object defaultValue = metaParam.getValue(property);
                if (defaultValue == null) {
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
        // do nothing.
    }


}
